package cn.js189.uqc.util;

import cn.js189.common.domain.Authentication;
import cn.js189.common.domain.Request;
import cn.js189.common.domain.Response;
import cn.js189.common.domain.ServiceClientException;
import cn.js189.common.util.Utils;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.UnsupportedEncodingException;


/**
 * 调用 bss-service-framework 的客户端封装类

 * 注意：此类非线程安全,每次使用请new一个实例再使用
 * 调用方法请查看 com.linkage.bss.serviceframework.client.Demo
 * @author zhaoxin
 */
public class ServiceClient {
    // 多线程共享一个httpClient实例
    private static HttpClient httpClient = new HttpClient(
            new MultiThreadedHttpConnectionManager());

    private String serviceUri = null;
    private Authentication auth = null;
    private Request request = null;
    private PostMethod postMethod = null;
    // 默认连接1分钟超时
    private static final int CONNECTION_TIMOUT_MS = 1000 * 10;
    static{
        httpClient.getHttpConnectionManager().getParams().setDefaultMaxConnectionsPerHost(500);       
        httpClient.getHttpConnectionManager().getParams().setMaxTotalConnections(500);       
        httpClient.getHttpConnectionManager().getParams().setSoTimeout(60000);       
        httpClient.getHttpConnectionManager().getParams().setTcpNoDelay(true);       
        httpClient.getHttpConnectionManager().getParams().setLinger(1000);   
    }
    public ServiceClient(String serviceUri, Authentication auth) {
        this.serviceUri = serviceUri;
        this.auth = auth;
        this.request = new Request();
        this.postMethod = new PostMethod(this.serviceUri);
        httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(
        		CONNECTION_TIMOUT_MS);
    }

    public ServiceClient(String serviceUri, String sysId, String pwd) {
        this.serviceUri = serviceUri;
        this.auth = new Authentication(sysId, pwd);
        this.request = new Request();
        this.postMethod = new PostMethod(this.serviceUri);
    }

    public Response executeAsJson(String json) throws ServiceClientException {
        request.setEntityAsJson(json);
        return execute();
    }

    public Response executeAsObject(Object obj) throws ServiceClientException {
        request.setEntityAsObject(obj);
        return execute();
    }

    public Response executeAsXml(String xml) throws ServiceClientException {
        request.setEntityAsXml(xml);
        return execute();
    }

    public void setConnectionTimeout(int timeout) {
        httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(
                timeout);
    }
    
    public void setSoTimeout(int soTimeout)
    {
        httpClient.getHttpConnectionManager().getParams().setSoTimeout(soTimeout);
    }

    private Response execute() throws ServiceClientException {
        try {
            return execute2();
        } catch (HttpException e) {
            throw new ServiceClientException(
                    e.getMessage(), e);
        } catch (IOException e1) {
            throw new ServiceClientException(
                    e1.getMessage(), e1);
        }
    }

    private Response execute2() throws  IOException,
            ServiceClientException {
        setRequestHeader();
        setRequestBody();
        int statusCode = httpClient.executeMethod(postMethod);
        return new Response(postMethod, statusCode);
    }

    private void setRequestBody() throws ServiceClientException {
        if ("object".equals(request.getContentType())) {
            postMethod.addRequestHeader("Content-type","application/x-java-serialized-object");
            
            try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
            		ObjectOutputStream oos = new ObjectOutputStream(baos);	){
                
                oos.writeObject(request.getEntity());
                oos.flush();
                postMethod.setRequestEntity(new ByteArrayRequestEntity(baos
                        .toByteArray()));
            } catch (IOException e) {
                throw new ServiceClientException(
                        "设置HTTP POST的BODY 抛出IOException 异常:", e);
            } 

        } else if ("xml".equals(request.getContentType())) {

            postMethod.addRequestHeader("Content-type", "text/xml;charset="
                    + request.getCharset());
            try {
                postMethod.setRequestEntity(new StringRequestEntity(
                        (String) request.getEntity(), "text/xml", request
                                .getCharset()));
            } catch (UnsupportedEncodingException e) {
                throw new ServiceClientException(
                        "设置HTTP POST的xmlBODY 不支持字符集 异常:", e);
            }
        } else {
            postMethod.addRequestHeader("Content-type", "text/plain;charset="
                    + request.getCharset());
            try {
                postMethod.setRequestEntity(new StringRequestEntity(
                        (String) request.getEntity(), "text/plain", request
                                .getCharset()));
            } catch (UnsupportedEncodingException e) {
                throw new ServiceClientException(
                        "设置HTTP POST的jsonBODY 不支持字符集 异常:", e);
            }
        }
    }

    public void setRequestHeader() {
        String reqTime = Utils.getDateFormatString(auth.getReqTime());
        postMethod.setRequestHeader("bssSysId", auth.getSysId());
        postMethod.setRequestHeader("bssSign", Utils.md5(auth.getSysId()
                + reqTime + auth.getPwd()));
        postMethod.setRequestHeader("bssReqTime", reqTime);
    }

    public void release() {
        if (postMethod != null) {
            postMethod.releaseConnection();
            postMethod = null;
        }
    }
}

